#include "SceneNode.h"

#include "Object/NewObject.h"
#include "Scene/SceneNodeComponent.h"
#include "Scene.h"
#include "TransformComponent.h"

void RSceneNode::FirstTimeInitializeObject()
{
    //创建位移组件
    Transform=CreateAndAddComponent<RTransformComponent>();
}

TSharedObjectPtr<RTransformComponent> RSceneNode::GetTransform() const
{
    return Transform;
}

SVec3 RSceneNode::GetForwardDirection() const
{
    return Transform->GetForward();
}

SVec3 RSceneNode::GetUpDirection() const
{
    return Transform->GetUp();
}

SVec3 RSceneNode::GetRightDirection() const
{
    return Transform->GetRight();
}

SVec3 RSceneNode::GetAxisDirection(EAxis InAxis) const
{
    switch(InAxis)
    {
    case EAxis::X:
        return GetRightDirection();
    case EAxis::Y:
        return GetForwardDirection();
    case EAxis::Z:
        return GetUpDirection();
    default:
        return SVec3(0,0,0);
    }
}

void RSceneNode::SetWorldPosition(const SVec3 &InPosition)
{
    Transform->SetPosition(InPosition);
}

SVec3 RSceneNode::GetWorldPosition()
{
    return Transform->GetPosition();
}

void RSceneNode::SetWorldRotation(const SQuat &InRotation)
{
    Transform->SetRotation(InRotation);
}

SQuat RSceneNode::GetWorldRotation()
{
    return Transform->GetRotation();
}

SMatrix4x4 RSceneNode::GetWorldTransform()
{
    return Transform->GetWorldMatrix();
}

void RSceneNode::SetEulerAngleInDegree(const SVec3 &InEulerAngleInDegree)
{
    Transform->SetEulerAngleInDegree(InEulerAngleInDegree);
}

void RSceneNode::SetScale(const SVec3& InScale)
{
    Transform->SetScale(InScale);
}

SVec3 RSceneNode::GetEulerAngleInDegree() const
{
    return Transform->GetEulerAngleInDegree();
}

SVec3 RSceneNode::GetScale()
{
    return Transform->GetScale();
}

SMatrix4x4 RSceneNode::GetViewMatrix()
{
    //auto Target=WorldPosition+GetForwardDirection();
    return SMatrix4x4::LookAt(
        GetWorldPosition(),
        GetForwardDirection(),
        GetUpDirection()
        );//.ToGlm();

}

void RSceneNode::BeginPlay()
{
    OnBeginPlay();
    bPlaying=true;

    for(auto& Component:Components)
    {
        Component->BeginPlay();
    }
}

void RSceneNode::EndPlay()
{
    assert(bPlaying);
    OnEndPlay();
    bPlaying=false;

    for(auto& Component:Components)
    {
        Component->EndPlay();
    }

}


void RSceneNode::Tick(float DeltaSeconds)
{
    OnTick(DeltaSeconds);

    for(auto& Component:Components)
    {
        Component->Tick(DeltaSeconds);
    }
}

void RSceneNode::Paint(CRenderScene* InRenderSceneData2)
{
    for(auto& Component:Components)
    {
        Component->Paint(InRenderSceneData2);
    }

}

void RSceneNode::AddSubNode(RSceneNodePtr InSubNode,bool bKeepWorldPosRotationScale)
{
    this->GetTransform()->AddChild(InSubNode->GetTransform(),bKeepWorldPosRotationScale);
}

void RSceneNode::RemoveSubNode(RSceneNodePtr InSubNode)
{
    this->GetTransform()->RemoveChild(InSubNode->GetTransform());
}

void RSceneNode::RemoveFromParent()
{
    auto Parent=GetTransform()->GetParent();
    if(!Parent)
    {
        return;
    }
    Parent->RemoveChild(GetTransform());
}

bool RSceneNode::HasSubNode() const
{
    return GetTransform()->GetChildrenNum()>0;
}

int64_t RSceneNode::GetChildrenNum() const
{
    return GetTransform()->GetChildrenNum();
}

TSharedObjectPtr<RSceneNode> RSceneNode::GetChild(int64_t Index) const
{
    return GetTransform()->GetChild(Index)->GetNode();
}

bool RSceneNode::IsRootNode() const
{
    return !Transform->GetParent();
}

TSharedObjectPtr<RScene> RSceneNode::GetScene()
{
    auto Owner=GetOwner();
    if(Owner->IsA(RScene::StaticClass()))
    {
        return Owner;
    }
    return nullptr;
}

TSharedObjectPtr<RSceneNodeComponent> RSceneNode::CreateAndAddComponent(
    const TSubclassOf<RSceneNodeComponent>& InComponentClass)
{
    auto Component=NewObject<RSceneNodeComponent>(InComponentClass.Get(),this);
    if(bPlaying)
    {
        Component->BeginPlay();
    }
    Components.Add(Component);
    return Component;
}

void RSceneNode::AddComponent(TSharedObjectPtr<RSceneNodeComponent> InComponent)
{
    InComponent->SetOwner(this);
    if(bPlaying)
    {
        InComponent->BeginPlay();
    }
    Components.Add(InComponent);
}

void RSceneNode::RemoveComponent(TSharedObjectPtr<RSceneNodeComponent> InComponent)
{
    if(InComponent->GetOwner()!=this)
    {
        //not belong to this node
        return;
    }

    InComponent->EndPlay();
    InComponent->SetOwner(nullptr);
    Components.Remove(InComponent);
}

void RSceneNode::RemoveAllComponents()
{
    for(auto& Component:Components)
    {
        Component->SetOwner(nullptr);
    }
    Components.Clear();
}

TSharedObjectPtr<RSceneNodeComponent> RSceneNode::GetComponent(CClass* ComponentClass)
{
    for(auto& Component: Components)
    {
        if(Component->GetClass()->CanObjectConvertTo(ComponentClass))
        {
            return Component;
        }
    }
    return nullptr;
}

TSharedObjectPtr<RSceneNodeComponent> RSceneNode::GetComponent(const TSubclassOf<RSceneNodeComponent> &InComponentClass)
{
    return GetComponent(InComponentClass.Get());
}

TVector<TSharedObjectPtr<RSceneNodeComponent>> RSceneNode::GetComponents(CClass *InComponentClass)
{
    TVector<TSharedObjectPtr<RSceneNodeComponent>> Ret;
    for(auto& Component: Components)
    {
        if(Component->GetClass()->CanObjectConvertTo(InComponentClass))
        {
            Ret.Add(Component);
        }
    }
    return Ret;
}

TVector<TSharedObjectPtr<RSceneNodeComponent>> RSceneNode::GetComponents(const TSubclassOf<RSceneNodeComponent> &InComponentClass)
{
    return GetComponents(InComponentClass.Get());
}
